上一篇,帶大家透過「Chat 模式」,讓自身變成客服機器人的方式。這篇會從「Webhook 模式」切入,並以 NestJS 作為後端框架,教你如何在休息時間,也能讓後端伺服器當你的替身幫你解惑使用者的問題。
本文將著重於實現以下核心功能:
本日程式碼的範例連結
預設 webhook 功能是關閉,需要設置完 webhook URL 才可以啟用這個功能
這邊會讓你選擇 Provider,這邊我選擇使用 Day1 創建的
Antonio(2025 IT 鐵人賽專用)
Webhook URL 用於建立後端伺服器與 Line 平台的連結關係
完成 webhook Provider 設定後,如果回到 LINE Response Setting 頁面,你會發現原本說明已經改變,這表示已經進入到 webhook 設定的最後一個步驟:設置 webhook URL。
接下來的所有設定操作都會改在 LINE Developers 平台進行。雖然 webhook URL 在 LINE Developers 和 LINE Official Account Manager 兩個平台都可以設定,但我們選擇在 LINE Developers 進行的主要原因是在 Line developers 有驗證連線的按鈕可以確認連線是否成功。
LINE Official Account Response settings 說明改變
首先,這邊會帶著大家理解 Line 平台與後端伺服器的關係,後端伺服器就像是一個 24hr 不休息的員工,我們可以讓他跟 Line 平台連接,藉此達到即使不在電腦前面,當收到 Line 官方帳號的相關事件(新朋友加入、封鎖、傳送訊息)伺服器也能代替我們處理。
不僅如此,伺服器能夠按照我們所想的邏輯或是搭配 AI 回覆,讓回覆使用者的內容不要那麼像是罐頭訊息,也可以根據不同的使用情境(例如:聖誕節)動態調整回覆訊息的風格。
以下是 Line 平台跟後端伺服器搭配運作的關係圖:
LINE 官方帳號訊息處理採用「事件驅動」的架構,透過 Webhook 機制實現即時的雙向溝通。
(白話解釋: Webhook URL 就像是你的官方帳號頻道與 LINE 平台之間的專屬橋樑。當有使用者與官方帳號互動時,LINE 平台會主動透過這座橋樑將訊息推送到你的伺服器進行處理,而不需要你的伺服器不斷詢問「有沒有新訊息?」
LINE 傳送訊息流程解釋:
- 用戶發送訊息:使用者透過 LINE 應用程式發送一則訊息。
- LINE 平台事件轉發:當使用者與 LINE 官方帳號互動時,LINE 平台會根據綁定的 Channel ID(頻道識別碼),將事件資料以 HTTP POST 方式傳送至頻道設定的 Webhook URL。伺服器接收到這些事件後,可以進行相對應的處理與回應。
- 伺服器回應處理:當伺服器完成訊息處理後,透過 LINE Messaging API 將回應訊息傳送回 LINE 平台,最終傳達給使用者。
前期會搭配使用 ngrok 的方式,方便大家快速看到 Demo 成效
首先,透過 NestJS CLI 快速建立後端的環境,讓大家快速體會使用 webhook 與 LineBot 交互的感覺。
使用的環境配置如下:
- nodejs 版本:v20.18.1
- nvm 版本:0.39.1
選擇你喜歡的套件管理器:如果不知道用哪一個推薦使用
pnpm
pnpm add -g @nestjs/cli
nest new linebot-webhook-server // 創建一個 nest專案,名稱為 linebot-webhook-server
看到終端機寫著Successfully created
代表已經創立好專案囉!
cd linebot-webhook-server // 進入這個創建完的資料夾中
pnpm run start:dev // 啟動這個 nest 專案(支援熱重載功能)
看到終端機寫著Nest application successfully started
代表伺服器成功啟動!
預設伺服器啟動的網址是:http://localhost:3000
(看到 hello world 就代表順利透過後端伺服器取回預設回傳值)
記得先關掉伺服器(control + c),再繼續安裝
pnpm install @line/bot-sdk --save
白話解釋:私人秘密不想被別人看到的檔案
pnpm install @nestjs/config
.env
並且在其中貼上以下兩行
- LINE_CHANNEL_SECRET:驗證來問候後端伺服器的是 Line Platform
- LINE_CHANNEL_ACCESS_TOKEN:驗證你是頻道授權者,可以透過 Bot 與使用者互動
LINE_CHANNEL_ACCESS_TOKEN=你的 Line Bot 頻道存取權杖
LINE_CHANNEL_SECRET=你的 Line Bot 頻道密鑰
1️⃣ LINE_CHANNEL_SECRET 放在 Basic settings 頁籤:
2️⃣ LINE_CHANNEL_ACCESS_TOKEN 放在 Messaging API 頁籤:
將檔案結構調整成最簡單能運行的方式,在逐漸優化!
app.controller.spec
及app.service
的部分line.config.ts 內容
import { ClientConfig } from '@line/bot-sdk';
import { ConfigService } from '@nestjs/config';
// 註冊名稱
export const LINE_CONFIG = 'LINE_CONFIG';
// 註冊的常數(這邊讀取的是.env 環境變數裡面的內容)
const lineConfig = (configService: ConfigService): ClientConfig => ({
channelAccessToken:
configService.get<string>('LINE_CHANNEL_ACCESS_TOKEN') ?? '',
channelSecret: configService.get<string>('LINE_CHANNEL_SECRET') ?? '',
});
// 匯出成 NestJS Provider 供依賴注入系統使用(讓 NestJS 可以透過 DI 容器管理此設定)
export const LineConfigProvider = {
provide: LINE_CONFIG,
useFactory: (configService: ConfigService) => lineConfig(configService),
inject: [ConfigService],
};
src/app.module.ts:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { ConfigModule } from '@nestjs/config';
import { LineConfigProvider } from '../config/line.config';
@Module({
imports: [ConfigModule.forRoot({ isGlobal: true })],
controllers: [AppController],
providers: [LineConfigProvider],
})
export class AppModule {}
這裡設定的路由前綴為 /webhook,這是專門用來接收 Line Bot webhook 事件的端點路徑。當 Line 平台有訊息或事件需要推送給我們的應用程式時,會透過這個特定的路由路徑進行回調通知
src/app.controller.ts:
import { Body, Controller, Inject, Post } from '@nestjs/common';
import {
ClientConfig,
WebhookRequestBody,
messagingApi,
MessageEvent,
WebhookEvent,
} from '@line/bot-sdk';
import { LINE_CONFIG } from '../config/line.config';
@Controller()
export class AppController {
private readonly lineClient: messagingApi.MessagingApiClient;
// 根據配置檔案初始化 LINE Messaging API 客戶端
constructor(@Inject(LINE_CONFIG) private readonly lineConfig: ClientConfig) {
this.lineClient = new messagingApi.MessagingApiClient({
channelAccessToken: this.lineConfig.channelAccessToken,
});
}
@Post('/webhook')
handleWebhook(@Body() body: WebhookRequestBody): Promise<string> {
console.log('Webhook 從 line platform 接收到的資訊:', body);
return 'Webhook processed successfully';
}
}
Line Webhook URL 必須是 HTTPS 網址,所以這邊需要搭配 ngrok 使用
ngrok
是一個反向代理工具,能夠將本地端口轉發至公開的 HTTPS 網址。它提供第三方代理服務,讓外部用戶可以直接透過公開網址存取您本地運行的應用程式
使用指令非常簡單,僅需要打開終端機輸入以下指令:
ngrok http 3000
👇 看到以下顯示的樣子就代表已經成功建立公開網址
這個https://d120-49-213-168-27.ngrok-free.app
就是 webhook URL
這種方式屬於臨時伺服器的方式,連結有時候會斷開。如果重新開啟 ngrok 需要記得 https 連結位置會改變,不要忘記連帶改動設定的 webhook URL
登入 Line developers 後,在 Message API Webhook settings
可以設定 Webhook URL:
特別注意要加上
/webhook
的路徑前綴,這是我們後端設置接收 Line 事件的端點,這樣才可以正確打到架設的後端伺服器喔!
按下Verify 按鈕
的同時,Line 平台就會發送事件到你的後端伺服器,這時候只要後端伺服器回傳的請求狀態碼是 200,畫面上會顯示 Success。這時候就代表已經成功建立 Line 平台與你架設的後端伺服器之間的連結。
在剛才設置 Webhook URL 的下方,找到並開啟「Use webhook」開關。此設定會與先前在 Line Official Account Manager 中的配置進行連動。
開啟後,你可以試著在官方帳號輸入訊息,查看終端機印出的訊息:
印出收到的訊息,代表你已經能成功接收到使用者跟你的 Line 官方帳號互動的事件囉!
LineBot 與使用者的互動包含多種事件類型,這邊舉例三種最常見的事件:
本系列的文章將聚焦於最常見的使用情境 Message event。
在下面程式碼中,我們會特別展示一個重要概念:每則訊息都帶有專屬的 Reply Token(可以理解為該訊息的「身分證」)。後端伺服器可以利用這個 Token 來回應特定的用戶訊息,這就是 Line Bot 回覆訊息的第一種方式 Reply Message。
src/app.controller.ts:
//...略
// 限制型別先收斂成部分事件,搭配 eventHandler 使用
type HandlerMap = {
[K in WebhookEvent['type']]: (
event: Extract<WebhookEvent, { type: K }>,
) => Promise<void>;
};
@Controller()
export class AppController {
private readonly lineClient: messagingApi.MessagingApiClient;
// 根據配置檔案初始化 LINE Messaging API 客戶端
constructor(@Inject(LINE_CONFIG) private readonly lineConfig: ClientConfig) {
this.lineClient = new messagingApi.MessagingApiClient({
channelAccessToken: this.lineConfig.channelAccessToken,
});
}
/**
* Webhook 端點處理器
* 接收來自 LINE Platform 的事件通知
* @param body LINE Platform 傳送的 Webhook 請求本體
* @returns 處理完成的回應訊息
*/
@Post('/webhook')
async handleWebhook(@Body() body: WebhookRequestBody): Promise<string> {
console.log('Webhook 從 line platform 接收到的資訊:', body);
const { events } = body;
// 事件處理器映射表
const eventHandler = {
message: (event) => this.handleMessageEvent(event),
} satisfies Partial<HandlerMap>;
// 逐項處理每一個事件
for (const event of events) {
const { type } = event;
if (type === 'message') {
await eventHandler[type](event);
}
}
return 'Webhook processed successfully';
}
/**
* 處理訊息事件
* @param event 訊息事件物件,包含用戶訊息內容(text)和回覆憑證(replyToken)
*/
private async handleMessageEvent(event: MessageEvent): Promise<void> {
const { replyToken } = event;
console.log('收到訊息事件', event);
console.log('訊息憑證(身分證):', replyToken);
await this.lineClient.replyMessage({
replyToken,
messages: [
{
type: 'text',
text: 'hello world',
},
],
});
}
}
輸入完訊息,就會看到你的專屬代理人回覆 hello world!的訊息 ⭐⭐⭐
這邊眼尖的你可能會發現我這邊沒使用到 LINE_CHANNEL_SECRET 也可以使用 Line Bot webhook 的功能,那為什麼還需要這個呢?明天的文章帶你揭曉